"""
主题: 以编程方式定义类
问题: 你在写一段代码，最终需要创建一个新的类对象。你考虑将类的定义源代码以字符串的形式发布出去。 
        并且使用函数比如 exec() 来执行它，但是你想寻找一个更加优雅的解决方案。
提示 : 
"""

# stock.py
# Example of making a class manually from parts

# Methods
def __init__(self, name, shares, price):
    self.name = name
    self.shares = shares
    self.price = price

def cost(self):
    return self.shares * self.price

cls_dict = {
    '__init__' : __init__,
    'cost' : cost,
}

# Make a class
import types
import abc
import collections

def recipe1():
    stock = types.new_class('Stock', (), {}, lambda ns: ns.update(cls_dict))
    stock.__module__ = __name__
    print(f"{stock = }")
    print(f"{type(stock) = }")

    s = stock('ACME', 50, 91.1)
    print(f"{s = }")
    print(f"{s.cost() = }")

def recipe2():
    '''使用元类'''
    stock = types.new_class('Stock', (), {'metaclass': abc.ABCMeta}, lambda ns: ns.update(cls_dict))
    stock.__module__ = __name__
    print(f"{stock = }")
    print(f"{type(stock) = }")

    s = stock('ACME', 50, 91.1)

    print(f"{s = }")
    print(f"{s.cost() = }")

def recipe3():
    stock = collections.namedtuple('Stock', ['name', 'shares', 'price'])
    stock.__module__ = __name__
    print(f"{stock = }")
    print(f"{type(stock) = }")

    s = stock('ACME', 50, 91.1)

    print(f"{s = }")

def main():
    print('recipe1'.center(20, '*'))
    recipe1()
    print('recipe2'.center(20, '*'))
    recipe2()
    print('recipe3'.center(20, '*'))
    recipe3()

if __name__ == '__main__':
    main()                